package com.baijiaoxi.common.utils.jdk;

import com.baijiaoxi.common.model.base.CoreException;
import com.baijiaoxi.common.model.file.FileSize;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;

/**
 * Created by shixj on 2017/8/21.
 */
@Slf4j
public class FileUtil {
    public static Long getFileSizeKB(String filepath) {
        File file = new File(filepath);
        return getFileSizeKB(file);
    }

    public static Long getFileSizeKB(File file) {
        return MathUtil.roundUp(file.length() / 1024.0, 0).longValue();
    }

    public static FileSize getFileSize(Long sizeKB) {
        return new FileSize(sizeKB);
    }

    public static FileSize getFileSize(String filepath) {
        return getFileSize(new File(filepath));
    }

    public static FileSize getFileSize(File file) {
        Long sizeKB = getFileSizeKB(file);
        return new FileSize(sizeKB);
    }

    /**
     * 删除单个文件
     *
     * @param fileName
     * @return
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        boolean res = false;
        // 如果文件路径所对应的文件存在，并且是一个文件，则直接删除
        if (file.exists() && file.isFile())
            res = file.delete();
        if (!res)
            log.error("文件删除失败{}", fileName);
        return res;
    }

    public static void copyFile(String oldPath, String newPath) {
        long starttime = System.currentTimeMillis();
        ByteBuffer bb = ByteBuffer.allocate(100000);
        File oldf = new File(oldPath);
        if (!oldf.exists() || !oldf.isDirectory()) {
            log.error("文件夹{}不存在", oldPath);
            return;
        }
        File newf = new File(newPath);
        if (!newf.exists()) {
            newf.mkdirs();
        } else if (!newf.isDirectory()) {
            log.error("目录{}已存在且为文件", newPath);
            return;
        }
        int len = -1;
        File temp = null;
        String[] list = oldf.list();
        if (list == null) {
            log.error("文件夹{}下没有文件", oldPath);
            return;
        }
        for (int i = 0; i < list.length; i++) {
            if (oldPath.endsWith(File.separator)) {
                temp = new File(oldPath + list[i]);
            } else {
                temp = new File(oldPath + File.separator + list[i]);
            }
            System.out.println(temp.isFile());
            if (temp.isFile()) {
                try (FileInputStream fis = new FileInputStream(temp);
                     FileChannel in = fis.getChannel();
                     FileOutputStream fos = new FileOutputStream(newf + "/" + temp.getName().toString());
                     FileChannel out = fos.getChannel();
                ) {
                    while ((len = in.read(bb)) != -1) {
                        bb.flip();
                        out.write(bb);
                        bb.clear();
                    }
                } catch (Exception e) {
                    log.error("文件读取失败", e);
                }

            }
            if (temp.isDirectory()) {
                copyFile(oldPath + "/" + list[i], newPath + "/" + list[i]);
            }
        }
        long endtime = System.currentTimeMillis();
        log.info("复制总时长：{}毫秒", endtime - starttime);
    }

    /**
     * 读取文件内容
     *
     * @param filePath
     * @return
     */
    public static String read(String filePath) {
        BufferedReader br = null;
        String line = null;
        StringBuffer buf = new StringBuffer();

        try {
            // 根据文件路径创建缓冲输入流
            br = new BufferedReader(new FileReader(filePath));

            // 循环读取文件的每一行, 对需要修改的行进行修改, 放入缓冲对象中
            while ((line = br.readLine()) != null) {
                buf.append(line);
                buf.append(System.getProperty("line.separator"));
            }
        } catch (Exception e) {
            log.error("文件读取失败", e);
        } finally {
            // 关闭流
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    br = null;
                }
            }
        }
        return buf.toString();
    }

    /**
     * 将内容回写到文件中
     *
     * @param filePath
     * @param content
     */
    public static void write(String filePath, String content) {
        BufferedWriter bw = null;

        try {
            // 根据文件路径创建缓冲输出流
            bw = new BufferedWriter(new FileWriter(filePath));
            // 将内容写入文件中
            bw.write(content);
        } catch (Exception e) {
            log.error("文件写入失败", e);
        } finally {
            // 关闭流
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    bw = null;
                }
            }
        }
    }

    public static void saveFile(byte[] file, String filePath, String fileName) {
        try {
            File targetFile = new File(filePath);
            if (!targetFile.exists()) {
                targetFile.mkdirs();
            }
            FileOutputStream out = new FileOutputStream(targetFile.getAbsolutePath() + File.separator + fileName);
            out.write(file);
            out.flush();
            out.close();
        } catch (Exception e) {
            log.error("保存文件至本地出错{}", fileName, e);
            throw new CoreException("保存文件至本地出错");
        }
    }

    public static String saveFile(InputStream is, String filePath, String fileName) {
        return write(is, filePath, fileName, true);
    }

    /**
     * 每次从最后位置续写
     *
     * @param is
     * @param filePath
     * @param fileName
     */
    public static String writeAppend(InputStream is, String filePath, String fileName) {
        return write(is, filePath, fileName, false);
    }

    public static String write(InputStream is, String filePath, String fileName, boolean writeOnStart) {
        if (is == null)
            throw new CoreException("无法读取文件流");
        File targetFileCatalog = new File(filePath);
        if (!targetFileCatalog.exists()) {
            targetFileCatalog.mkdirs();
        }
        String finalFileName = targetFileCatalog.getPath() + File.separator + fileName;
        try (
                BufferedInputStream in = new BufferedInputStream(is);
                FileOutputStream fos = new FileOutputStream(finalFileName, writeOnStart);
                BufferedOutputStream out = new BufferedOutputStream(fos);
        ) {
            byte[] b = new byte[1024];
            int len = 0;
            while ((len = in.read(b)) > 0) {
                // 写入数据 不能用 out.write(b); 那样会写入全部1024个字节，但是文件结尾最后一个数组大概率不满1024字节
                out.write(b, 0, len);
            }
        } catch (Exception e) {
            log.error("保存文件至本地出错{}", fileName, e);
            throw new CoreException("保存文件至本地出错");
        }
        return finalFileName;
    }

    public static void download(String url, String localPath) {
        try (ReadableByteChannel rbc = Channels.newChannel(new URL(url).openStream());
             FileOutputStream fos = new FileOutputStream(localPath)
        ) {
            fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
            log.info("文件下载成功: {}", localPath);
        } catch (IOException e) {
            log.error("文件下载失败: {}", localPath, e);
        }
    }
}
